package team.seekers.hula.user.user.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lly835.bestpay.enums.BestPayTypeEnum;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import team.seekers.hula.auth.service.MindUtils;
import team.seekers.hula.common.entity.PageResult;
import team.seekers.hula.common.entity.UserSessionDTO;
import team.seekers.hula.common.entity.enums.LoginTypeEnum;
import team.seekers.hula.common.entity.enums.user.RoleEnum;
import team.seekers.hula.common.entity.result.ApiException;
import team.seekers.hula.common.entity.result.ApiExceptionEnum;
import team.seekers.hula.common.utils.AssertUtils;
import team.seekers.hula.common.utils.BaseConvertUtils;
import team.seekers.hula.common.utils.JwtUtil;
import team.seekers.hula.teamwork.team.team.service.TeamService;
import team.seekers.hula.user.school.service.SchoolService;
import team.seekers.hula.user.user.entity.dao.UserDO;
import team.seekers.hula.user.user.entity.dto.*;
import team.seekers.hula.user.user.entity.vo.UserBasicInfoVO;
import team.seekers.hula.user.user.entity.vo.UserInfoVO;
import team.seekers.hula.user.user.entity.vo.UserPageListVO;
import team.seekers.hula.user.user.mapper.UserMapper;
import team.seekers.hula.user.userData.dao.UserDataDao;
import team.seekers.hula.user.userData.entity.dao.UserDataDO;
import team.seekers.hula.user.userData.service.UserDataService;
import team.seekers.hula.user.userFollow.dao.UserFollowDao;
import team.seekers.hula.user.userFollow.entity.dao.UserFollowerDO;
import team.seekers.hula.user.userFollow.entity.vo.UserFollowInfoVO;
import team.seekers.hula.user.userHula.service.UserHulaService;

import javax.annotation.Resource;
import java.util.*;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author hbc
 * @since 2021-02-15
 */
@Service
public class UserService extends ServiceImpl<UserMapper, UserDO> {

    @Resource
    private UserFollowDao userFollowDao;

    @Resource
    private SchoolService schoolService;

    @Resource
    private TeamService teamService;

    @Resource
    private UserDataDao userDataDao;

    @Resource
    private UserDataService userDataService;

    @Resource
    @Lazy
    private UserHulaService userHulaService;

    @Resource
    private JwtUtil jwtUtil;

    public UserDO selectByName(String username) {
        QueryWrapper<UserDO> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username);
        UserDO userDO = getOne(wrapper);
        return userDO;
    }

    public void ban(String userId) {
        UserDO userDO = UserDO.builder()
                .id(userId)
                .role(RoleEnum.BANNED)
                .build();
        updateById(userDO);
        /* 删除token以更新token，先更新数据库再删redis优先保证数据库的数据最新
         * 若redis宕机等问题导致数据不一致，通过查看日志手动解决
         */
        jwtUtil.refreshToken(userDO);
    }

    public void unban(String userId) {
        UserDO userDO = UserDO.builder()
                .id(userId)
                .role(RoleEnum.USER)
                .build();
        updateById(userDO);
        /* 删除token以更新token，先更新数据库再删redis优先保证数据库的数据最新
         * 若redis宕机等问题导致数据不一致，通过查看日志手动解决
         */
        jwtUtil.refreshToken(userDO);
    }

    public void setV(String userId) {
        UserDO userDO = UserDO.builder()
                .id(userId)
                .role(RoleEnum.WAVER)
                .build();
        updateById(userDO);
        /* 删除token以更新token，先更新数据库再删redis优先保证数据库的数据最新
         * 若redis宕机等问题导致数据不一致，通过查看日志手动解决
         */
        jwtUtil.refreshToken(userDO);
    }

    public UserSessionDTO add(UserCreateDTO userCreateDTO) {
        UserDO userDO = UserDO.builder().build();
        BeanUtils.copyProperties(userCreateDTO, userDO);
        userDO.setRole(RoleEnum.USER);
        save(userDO);

        // 生成数据记录
        UserDataDO userDataDO = new UserDataDO();
        userDataDO.setId(userDO.getId());
        userDataDao.save(userDataDO);

        if(StringUtils.isNotBlank(userCreateDTO.getMiniOpenId())){
            checkAndBindOpenId(userCreateDTO.getUnionId(),userCreateDTO.getMiniOpenId(),LoginTypeEnum.MINI_APP);
        }

        // 如果有提问则生成提问
//        ArrayList<PointAddDTO> pointAddDTOS = new ArrayList<>();
//        pointAddDTOS.add(PointAddDTO.builder()
//                .keyword(userCreateDTO.getPoint())
//                .answer(AnswerAddDTO.builder().content(userCreateDTO.getAnswer()).build()).build());
//
//        questionService.add(QuestionAddDTO.builder()
//                .title(userCreateDTO.getTitle())
//                .pointAddDTOList(pointAddDTOS).build()
//        );

        return UserDO.toSessionDTO(userDO);

    }

    @Transactional(rollbackFor = Exception.class)
    public UserSessionDTO toBeHula(UserHulaDTO userHulaDTO) {
        UserDO userDO = getById(userHulaDTO.getId());
        userDO.setRole(RoleEnum.HULAER);
        updateById(userDO);

        userHulaService.createHular(userHulaDTO);
        return UserDO.toSessionDTO(userDO);
    }

    public void update(UserUpdateDTO userUpdateVO) {
        UserDO userDO = UserDO.builder().build();
        BeanUtils.copyProperties(userUpdateVO, userDO);
        userDO.setId(MindUtils.getUserId());
        updateById(userDO);
        /* 删除token以更新token，先更新数据库再删redis优先保证数据库的数据最新
         * 若redis宕机等问题导致数据不一致，通过查看日志手动解决
         */
        jwtUtil.refreshToken(userDO);
    }

    public UserInfoVO getInfoVO(String userId) {
        UserDO userDO = getById(userId);
        if (userDO == null) {
            return null;
        }
        UserInfoVO userInfoVO = new UserInfoVO();
        BeanUtils.copyProperties(userDO, userInfoVO);
        userInfoVO.setRole(userDO.getRole().getType());

        return userInfoVO;
    }

    public UserBasicInfoVO getBasicInfoVO(String userId) {
        UserDO userDO = getById(userId);
        UserBasicInfoVO userInfoVO = new UserBasicInfoVO();
        BeanUtils.copyProperties(userDO, userInfoVO);
        return userInfoVO;
    }

    public UserDO getByPwd(String tel, String account, String password) {

        QueryWrapper<UserDO> wrapper = new QueryWrapper<UserDO>().eq("password", password);

        if (StringUtils.isNotBlank(tel)) {
            wrapper.eq("tel", tel);
        }

        if (StringUtils.isNotBlank(account)) {
            wrapper.eq("account", account);
        }

        List<UserDO> userDOList = list(wrapper);

        if (userDOList == null || userDOList.size() == 0) {
            return null;
        }
        if (userDOList.size() != 1) {
            throw new ApiException(ApiExceptionEnum.SYSTEM_FAIL, "出现用户重复");
        }
        return userDOList.get(0);
    }

    public UserDO getByTelAndPwd(String tel, String password) {
        List<UserDO> userDOList = list(new QueryWrapper<UserDO>().eq("tel", tel).eq("password", password));
        if (userDOList == null || userDOList.size() == 0) {
            return null;
        }
        if (userDOList.size() != 1) {
            throw new ApiException(ApiExceptionEnum.SYSTEM_FAIL, "出现用户重复");
        }
        return userDOList.get(0);
    }

    public PageResult<UserPageListVO> page(UserPageQueryDTO userQuery) {
        LambdaQueryChainWrapper<UserDO> query = lambdaQuery();

        if (StringUtils.isNotBlank(userQuery.getNickName())) {
            query.like(UserDO::getNickname, userQuery.getNickName());
        }

        if (StringUtils.isNotBlank(userQuery.getTel())) {
            query.like(UserDO::getTel, userQuery.getTel());
        }

        if (userQuery.getRole() != null) {
            query.eq(UserDO::getRole, userQuery.getRole());
        }

        Page<UserDO> pageResult = query.page(new Page<>(userQuery.getPageNow(), userQuery.getPageSize()));

        ArrayList<UserPageListVO> userDTOS = new ArrayList<>();
        for (UserDO record : pageResult.getRecords()) {
            UserPageListVO userPageListVO = new UserPageListVO();
            BeanUtils.copyProperties(record, userPageListVO);
            userPageListVO.setRole(record.getRole().getCnName());
            userDTOS.add(userPageListVO);
        }

        return new PageResult<>(pageResult.getTotal(), userDTOS);
    }

    public Map<String, UserInfoDTO> userIdsToUserInfoDTOs(List<String> userIds) {
        HashMap<String, UserInfoDTO> map = new HashMap<>();
        for (UserDO userDO : lambdaQuery().in(UserDO::getId, userIds).list()) {
            UserInfoDTO userInfoDTO = new UserInfoDTO();
            BeanUtils.copyProperties(userDO, userInfoDTO);
            map.put(userDO.getId(), userInfoDTO);
        }

        return map;
    }

    public String queryRoleById(String userId) {
        UserDO userDO = this.lambdaQuery().select(UserDO::getRole).eq(UserDO::getId, userId).one();
        return Optional.ofNullable(userDO).map(UserDO::getRole).map(RoleEnum::getName).orElse(null);
    }

    public Map<String, UserInfoDTO> userIdsToUserInfoDTOs(ArrayList<String> userIds) {
        HashMap<String, UserInfoDTO> map = new HashMap<>();
        for (UserDO userDO : lambdaQuery().in(UserDO::getId, userIds).list()) {
            UserInfoDTO userInfoDTO = new UserInfoDTO();
            BeanUtils.copyProperties(userDO, userInfoDTO);
            map.put(userDO.getId(), userInfoDTO);
        }
        return map;
    }

    public List<String> queryAllCollectCourse(String userId) {
        return null;
    }

    public Boolean collectCourseOrCancel(String userId, String courseId) {
        return null;
    }

    public void followOrUnFollowUser(String followerId, String userId) {
        Integer count = userFollowDao
                .lambdaQuery()
                .eq(UserFollowerDO::getUserId, userId)
                .eq(UserFollowerDO::getFollowerId, followerId)
                .count();
        if (count > 0) {
            userFollowDao.lambdaUpdate().eq(UserFollowerDO::getUserId, userId).eq(UserFollowerDO::getFollowerId, followerId).remove();
            userDataService.changeFollowerCount(-1,userId);
        } else {
            userFollowDao.save(UserFollowerDO.builder().userId(userId).followerId(followerId).build());
            userDataService.changeFollowerCount(1,userId);
        }
    }

    public UserFollowInfoVO getFollowInfoVO(String userId, String followerId) {
        UserDO userDO = getById(userId);
        if (userDO == null) {
            return null;
        }
        UserFollowInfoVO userInfoVO = new UserFollowInfoVO();
        BeanUtils.copyProperties(userDO, userInfoVO);
        if(followerId != null){
            userInfoVO.setIsFollowed(userFollowDao.lambdaQuery().eq(UserFollowerDO::getUserId, userId).eq(UserFollowerDO::getFollowerId, followerId).count() > 0);
        }
        return userInfoVO;
    }

    public void checkAndBindOpenId(String unionId, String openId, LoginTypeEnum loginType) {
        UserDO userDO = lambdaQuery().eq(UserDO::getUnionId, unionId).one();

        if (StringUtils.isNotBlank(userDO.getOpenIdMap())) {
            Map<String, String> map = BaseConvertUtils.stringToMap(userDO.getOpenIdMap());
            if (map != null && map.get(loginType.getValue()) == null) {
                map.put(loginType.getValue(), openId);
                userDO.setOpenIdMap(BaseConvertUtils.mapToString(map));
                updateById(userDO);
            }
        } else {
            Map<String, String> map = new HashMap<>();
            map.put(loginType.getValue(), openId);
            userDO.setOpenIdMap(BaseConvertUtils.mapToString(map));
            updateById(userDO);
        }
    }

    public UserDO getByUnionId(String unionId, String userAgent) {
        return lambdaQuery().eq(UserDO::getUnionId, unionId).one();
    }

    public UserSessionDTO getUserSessionByTel(String tel) {
        if (lambdaQuery().eq(UserDO::getTel, tel).count() > 0) {
            return UserDO.toSessionDTO(lambdaQuery().eq(UserDO::getTel, tel).one());
        } else {
            return null;
        }
    }

    public Map<String, UserBasicInfoVO> idsMapBasicInfoVOList(ArrayList<String> userIds) {
        List<UserDO> userDOList = lambdaQuery().select(
                UserDO::getId,
                UserDO::getNickname,
                UserDO::getAvatar
        ).in(UserDO::getId, userIds).list();

        HashMap<String, UserBasicInfoVO> map = new HashMap<>();

        for (UserDO userDO : userDOList) {



            map.put(userDO.getId(), UserBasicInfoVO.builder().id(userDO.getId()).nickname(userDO.getNickname()).avatar(userDO.getAvatar()).build());
        }

        return map;
    }

    public Map<String, UserFollowInfoVO> idsMapFollowInfoVOList(ArrayList<String> userIds,String userId) {
        List<UserDO> userDOList = lambdaQuery().select(
                UserDO::getId,
                UserDO::getNickname,
                UserDO::getAvatar
        ).in(UserDO::getId, userIds).list();

        HashMap<String, UserFollowInfoVO> map = new HashMap<>();

        for (UserDO userDO : userDOList) {

            UserFollowInfoVO userInfoVO = new UserFollowInfoVO();
            BeanUtils.copyProperties(userDO, userInfoVO);
            userInfoVO.setIsFollowed(
                    userFollowDao.lambdaQuery()
                            .eq(UserFollowerDO::getUserId, userDO.getId())
                            .eq(UserFollowerDO::getFollowerId, userId).count() > 0);
            map.put(userDO.getId(), userInfoVO);
        }

        return map;
    }

    public List<String> listIdsLikeNickname(String nickname) {
        List<UserDO> userDOList = lambdaQuery().select(
                UserDO::getId
        ).like(UserDO::getNickname, nickname).list();

        ArrayList<String> userIds = new ArrayList<>();
        for (UserDO userDO : userDOList) {
            userIds.add(userDO.getId());
        }

        return userIds;
    }

    public void bindWxAndPhone(String phone, String unionId) {
        UserDO userDO = lambdaQuery().eq(UserDO::getTel,phone).or().eq(UserDO::getUnionId,unionId).one();

        if (StringUtils.isBlank(userDO.getTel())) {
            userDO.setTel(phone);
        }

        if (StringUtils.isBlank(userDO.getUnionId())) {
            userDO.setUnionId(unionId);
        }

        updateById(userDO);
        /* 删除token以更新token，先更新数据库再删redis优先保证数据库的数据最新
         * 若redis宕机等问题导致数据不一致，通过查看日志手动解决
         */
//        jwtUtil.refreshToken(userDO);
    }

    public void bindAccount(String phone, String account) {
        if (isExistPhone(phone)) {
            // 手机账号已存在，绑定account即可
            UserDO userDO = lambdaQuery()
                    .select(UserDO::getId, UserDO::getAccount)
                    .eq(UserDO::getTel, phone).one();
            userDO.setAccount(account);
            updateById(userDO);
            /* 删除token以更新token，先更新数据库再删redis优先保证数据库的数据最新
             * 若redis宕机等问题导致数据不一致，通过查看日志手动解决
             */
            jwtUtil.refreshToken(userDO);
        } else {
            throw AssertUtils.newException(ApiExceptionEnum.CAPTCHA_NOT_FOUND, "系统异常，该用户不存在");
        }
    }

    public void bindPassword(String phone, String password) {
        if (isExistPhone(phone)) {
            // 手机账号已存在，绑定account即可
            UserDO userDO = lambdaQuery()
                    .select(UserDO::getId)
                    .eq(UserDO::getTel, phone).one();
            userDO.setPassword(password);
            updateById(userDO);
        } else {
            throw AssertUtils.newException(ApiExceptionEnum.CAPTCHA_NOT_FOUND, "系统异常，该用户不存在");
        }
    }

    public boolean isExistPhone(String phone) {
        return lambdaQuery().eq(UserDO::getTel, phone).count() > 0;
    }

    public boolean isExistUnionId(String unionId) {
        return lambdaQuery().eq(UserDO::getUnionId, unionId).count() > 0;
    }


    public void setSchoolId(String schoolId, String userId) {
        lambdaUpdate().set(UserDO::getSchoolId,schoolId).eq(UserDO::getId,userId).update();
    }


    public String queryOpenIdByIdAndPayType(String userId, BestPayTypeEnum payType) {
        String openIdMap = lambdaQuery().eq(UserDO::getId, userId).one().getOpenIdMap();
        if(BestPayTypeEnum.WXPAY_MINI.equals(payType)){
            return BaseConvertUtils.stringToMap(openIdMap).get(LoginTypeEnum.MINI_APP.getValue());
        }
        if(BestPayTypeEnum.WXPAY_MP.equals(payType)){
            return BaseConvertUtils.stringToMap(openIdMap).get(LoginTypeEnum.MP.getValue());
        }
        throw new ApiException(ApiExceptionEnum.COMMON_FAIL).message("openId不存在");
    }

    public List<String> userIdsToAvatarList(ArrayList<String> userIds) {
        List<UserDO> userDOS = lambdaQuery().select(UserDO::getAvatar).in(UserDO::getId, userIds).list();
        ArrayList<String> avatars = new ArrayList<>();
        for (UserDO userDO : userDOS) {
            avatars.add(userDO.getAvatar());
        }
        return avatars;
    }
}
